package org.javahispano.javacup.tacticas_aceptadas.jpautom.org.javahispano.javacup.tacticas.pitufos;
import org.javahispano.javacup.*;
import java.awt.Color;
import org.javahispano.javacup.modelo.Constantes;

/**
 *
 * @author Jesus
 */
public class Pase extends MiniTactica{

//	private EVDepurador debug;

	public void ejecutar() {
		// TODO: corregir comportamiento cuando dos jugadores hacen pase a la vez
//		if (debug==null) debug=EVDepuradorCreator.crearInstancia(Color.ORANGE);
		Campo c=getCampo();
		for (int i=0; i<11; i++) {
			if (Util.tieneBola(c, i)) {
				paseCompuesto2(i, c);
			}
		}
	}



	private int compararPases(double pEx1, double t1, Pos p1,
			  double pEx2, double t2, Pos p2) {
		if (pEx1>pEx2) return -1;
		else if (pEx2>pEx1) return 1;
		// transforma distancias a porteria contraria en 0..1
		Pos porteria=new Pos();
		porteria.setLocation(0.0, Constantes.LARGO_CAMPO_JUEGO/2);
		// transforma distancias en 0..1
		double pp1=Math.max(0.0, 1-porteria.distance2d(p1)/Constantes.LARGO_CAMPO_JUEGO);
		double pp2=Math.max(0.0, 1-porteria.distance2d(p2)/Constantes.LARGO_CAMPO_JUEGO);
		// transforma tiempos en 0..1
		double tt1=Math.max(0.0, 1-t1/90);
		double tt2=Math.max(0.0, 1-t2/90);
		// 90 es el tiempo aproximado en alcanzar el pase mas largo posible

		// 70-30
		if (70*pp1+30*tt1>70*pp2+30*tt2) return -1;
		else return 1;
	}


	private void paseCompuesto2(int jug, Campo c2) {
		Campo c=(Campo)c2.clone();
		c.getJugador(jug).setTSinRematar(Constantes.ITERACIONES_GOLPEAR_BALON);
		int NGRANOPHI=30;
		int NGRANOVXY=20;
		int NGRANOVZ=10;

		int NERRORPHI=(int)Math.max(1, Math.round(NGRANOPHI*c.getInfo().getP(jug)/(2*Math.PI))); // porciones que representa el error en phi del tiro

		int bfJugReceptores[][][]=new int[NGRANOVXY][NGRANOVZ][NGRANOPHI]; //fuerza maxima=7
		double pExitoSimple[][][]=new double[NGRANOVXY][NGRANOVZ][NGRANOPHI];
		double tInterSimple[][][]=new double[NGRANOVXY][NGRANOVZ][NGRANOPHI];
		Pos pInterSimple[][][]=new Pos[NGRANOVXY][NGRANOVZ][NGRANOPHI];

		int tInter[];
		int[] maxIvz=new int[NGRANOVXY];

		double pExitoMedio[][][]=new double[NGRANOVXY][NGRANOVZ][NGRANOPHI];
		double tInterMedio[][][]=new double[NGRANOVXY][NGRANOVZ][NGRANOPHI];
		Pos pInterMedio[][][]=new Pos[NGRANOVXY][NGRANOVZ][NGRANOPHI];

		double thMax=Math.PI*Constantes.ANGULO_VERTICAL_MAX/180;

		// Calcula pases simples
		for (int iphi=0; iphi<NGRANOPHI; iphi++) {
			for (int ivxy=0; ivxy<NGRANOVXY; ivxy++) {
				for (int ivz=0; ivz<NGRANOVZ; ivz++) {
					double phi=iToPhi(iphi, NGRANOPHI);
					double vxy=iToVxy(ivxy, NGRANOVXY, c.getInfo().getF(jug));
					double vz=iToVz(ivz);

					if (vz>vxy*Math.sin(Math.min(thMax, Math.acos(vxy/c.getInfo().getF(jug))))) {
						maxIvz[ivxy]=ivz;
						break;
					}

					c.getBola().getPos().z=0.0; //el inicio del remate empieza siempre en el suelo
					c.getBola().getMov().setVxyz(vxy*Math.cos(phi), vxy*Math.sin(phi), vz);

					Pos[] ps=new Pos[22];
					tInter=Util.intercepcionMultiple(c, ps, true);
					int tMin=Integer.MAX_VALUE;
					Pos pMin=new Pos();
					for (int i=0; i<22; i++) {
						if (tInter[i]!=0 && tInter[i]<tMin && Util.dentroCampo(ps[i])) {tMin=tInter[i]; pMin.set(ps[i]);}
					}
					tInterSimple[ivxy][ivz][iphi]=tMin;
					pInterSimple[ivxy][ivz][iphi]=pMin;

					int mask=0;
					int j=0, k=0;
					for (int i=0; i<22; i++) {
						if (tInter[i]==tMin) {
							mask|=1<<i;
							k++;
							if (i<11) j++;
						}
					}
					pExitoSimple[ivxy][ivz][iphi]=(k>0?(double)j/k:0.0);
					bfJugReceptores[ivxy][ivz][iphi]=mask;

/*					Pos p1=c.getBola().getPos();
					Pos p2=(Pos)p1.clone();
					p2.x+=10*pExitoSimple[f][iang]*Math.cos(iang*2*Math.PI/NGRANO);
					p2.y+=10*pExitoSimple[f][iang]*Math.sin(iang*2*Math.PI/NGRANO);
					debug.line(p1, p2, 1);
*/
				}
			}
		}

		// Calcula medias por error de pase
		for (int ivxy=0; ivxy<NGRANOVXY; ivxy++) {
			for (int ivz=0; ivz<maxIvz[ivxy]; ivz++) {
				pExitoMedio[ivxy][ivz]=meanFilter(pExitoSimple[ivxy][ivz], NERRORPHI);
				tInterMedio[ivxy][ivz]=meanFilter(tInterSimple[ivxy][ivz], NERRORPHI);
				pInterMedio[ivxy][ivz]=meanFilter(pInterSimple[ivxy][ivz], NERRORPHI);
			}
		}


		// Decide el mejor pase
		int mejorIphi=0, mejorIvxy=0, mejorIvz=0;
		for (int iphi=0; iphi<NGRANOPHI; iphi++) {
			for (int ivxy=0; ivxy<NGRANOVXY; ivxy++) {
				for (int ivz=0; ivz<maxIvz[ivxy]; ivz++) {
					if (compararPases(
							pExitoMedio[ivxy][ivz][iphi],
							tInterMedio[ivxy][ivz][iphi],
							pInterMedio[ivxy][ivz][iphi],
							pExitoMedio[mejorIvxy][mejorIvz][mejorIphi],
							tInterMedio[mejorIvxy][mejorIvz][mejorIphi],
							pInterMedio[mejorIvxy][mejorIvz][mejorIphi])<0) {
						mejorIvxy=ivxy;
						mejorIvz=ivz;
						mejorIphi=iphi;
					}
				}
			}
		}

		// Decide que jugadores van a buscar la bola y donde
		Pos[] destino=new Pos[11];
		double phi=iToPhi(mejorIphi, NGRANOPHI);
		double vxy=iToVxy(mejorIvxy, NGRANOVXY, c.getInfo().getF(jug));
		double vz=iToVz(mejorIvz);

		Util.log("mejorPase: [%d][%d][%d] exitoMed,tMedio=(%.2f %.2f)\n", mejorIvxy, mejorIvz, mejorIphi,
				  pExitoMedio[mejorIvxy][mejorIvz][mejorIphi], tInterMedio[mejorIvxy][mejorIvz][mejorIphi]);
		Util.log("mejorPase: [vxy=%.2f][vz=%.2f][phi=%.2f]\n", vxy, vz, phi);
//		for (int iphi=0; iphi<NGRANOPHI; iphi++) {
//			debug.line(c.getBola().getPos(), pInterSimple[0][0][iphi], 1, Color.getHSBColor(0f, 0f, (float)(pExitoSimple[0][0][iphi]*0.5+0.5)));
//		}

		c.getBola().getMov().setVxyz(vxy*Math.cos(phi), vxy*Math.sin(phi), vz);
		for (int i=0; i<11; i++) {
			Pos p=new Pos();
			Util.intercepcion(c, i, p);
			destino[i]=p;
		}

		// Crea comandos

		//debug
//		Pos p=(Pos)c.getBola().getPos().clone();
//		Pos p2=(Pos)p.clone();
//		p2.x+=100*Math.cos(phi);
//		p2.y+=100*Math.sin(phi);
//		debug.line(p, p2, 1);

		comandos.add(Util.crearComandoGolpearBalonZ(c, jug, vxy, vz, phi));
		for (int i=0; i<11; i++) {
			comandos.add(Util.crearComandoIrA(c, i, destino[i]));
//			debug.line(c.getJugador(i).getPos(), destino[i], 1);
		}
	}





	private double[] meanFilter(double[] a, int winSize) {
		double[] b=new double[a.length];
		double acum=0.0;
		int i;

		if (winSize>a.length) winSize=a.length;
		for (i=0; i<winSize; i++) acum+=a[i];
		b[winSize/2]=acum/winSize;
		for (i=1; i<a.length; i++) {
			acum=acum-a[i-1]+a[(winSize+i-1)%a.length];
			b[(winSize/2+i)%a.length]=acum/winSize;
		}
		return b;
	}

	private Pos[] meanFilter(Pos[] a, int winSize) {
		Pos[] b=new Pos[a.length];
		Pos pacum=new Pos();
		int i;

		if (winSize>a.length) winSize=a.length;
		for (i=0; i<winSize; i++) {
			pacum.x+=a[i].x;
			pacum.y+=a[i].y;
			pacum.z+=a[i].z;
		}
		b[winSize/2]=new Pos();
		b[winSize/2].setLocation(pacum.x/winSize, pacum.y/winSize, pacum.z/winSize);
		for (i=1; i<a.length; i++) {
			pacum.x=pacum.x-a[i-1].x+a[(winSize+i-1)%a.length].x;
			pacum.y=pacum.y-a[i-1].y+a[(winSize+i-1)%a.length].y;
			pacum.z=pacum.z-a[i-1].z+a[(winSize+i-1)%a.length].z;
			b[(winSize/2+i)%a.length]=new Pos();
			b[(winSize/2+i)%a.length].setLocation(pacum.x/winSize, pacum.y/winSize, pacum.z/winSize);
		}
		return b;
	}

	private double iToDouble(int i, int NGRANO, double min, double max) {
		return min+i*(max-min)/(NGRANO-1);
	}

	private double iToFz(int ifz, int NGRANOFZ, double fJug) {
		//return iToDouble(ifz, NGRANOFZ, Constantes.REMATE_VELOCIDAD_MIN, fJug);
		return iToDouble(ifz, NGRANOFZ, 1.0, fJug);
	}

	private double iToPhi(int iphi, int NGRANOPHI) {
		return iToDouble(iphi, NGRANOPHI, 0, 2*Math.PI);
	}

	private double iToTh(int ith, int NGRANOTH) {
		return iToDouble(ith, NGRANOTH, 0, Constantes.ANGULO_VERTICAL_MAX*Math.PI/180);
	}

	double iToVxy(int ivxy, int NGRANOVXY, double fJug) {
		return iToDouble(ivxy, NGRANOVXY, 0.6, fJug);
	}

	private double iToVz(int ivz) {
		if (ivz>0) ivz+=2; // minimo vz=3*G para subir al aire
		return Constantes.G*ivz;
	}

}
